package standalone

import (
	"github.com/DiracLee/dires-go/app/cmdline"
	"github.com/DiracLee/dires-go/app/config"
	"github.com/DiracLee/dires-go/app/connection"
	"github.com/DiracLee/dires-go/app/database"
	"github.com/DiracLee/dires-go/app/payload"
	"github.com/DiracLee/dires-go/app/server"
	"github.com/DiracLee/dires-go/logger"
	"github.com/DiracLee/dires-go/utils"
	"strings"
)

const (
	nDBs = 16
)

type Standalone struct {
	dbs        []database.DB
	hub        *Hub
	aofHandler *AOFHandler
}

func NewStandalone() server.Server {
	svr := &Standalone{}
	svr.dbs = make([]database.DB, nDBs)
	for i := range svr.dbs {
		singleDB := database.New()
		singleDB.SetIndex(i)
		svr.dbs[i] = singleDB
	}
	svr.hub = NewHub()
	if config.GetAppendOnly() {
		aofHandler, err := NewAOFHandler(svr, func() *Standalone {
			return NewBasicStandAlone()
		})
		if err != nil {
			logger.Error("NewAOFHandler failed")
			return svr
		}
		svr.aofHandler = aofHandler
		for _, db := range svr.dbs {
			singleDB := db
			singleDB.SetAddAOF(func(cmdLine [][]byte) {
				svr.aofHandler.AddAof(singleDB.GetIndex(), cmdLine)
			})
		}
	}
	return svr
}

func NewBasicStandAlone() *Standalone {
	svr := &Standalone{}
	svr.dbs = make([]database.DB, nDBs)
	for i := range svr.dbs {
		svr.dbs[i] = database.NewSample()
	}
	return svr
}

func (svr *Standalone) Execute(conn connection.Connection, cmdLine cmdline.CmdLine) payload.Payload {
	if len(cmdLine) == 0 {
		return payload.NewNoPayload()
	}
	cmdType := strings.ToLower(string(cmdLine[0]))

	logger.Debugf("%s %v", cmdType, strings.Join(utils.BytesSliceToStrings(cmdLine[1:]), " "))

	if cmdType == cmdline.CmdAuth {
		return handleAuth(conn, cmdLine[1:])
	}
	if !isAuthenticated(conn) {
		return payload.NewErrPayload("NOAUTH Authentication required")
	}
	handleFunc, ok := handlers[cmdType]
	if !ok {
		return execNormal(svr, conn, cmdLine)
	}
	return handleFunc(svr, conn, cmdLine[1:])
}

func (svr *Standalone) Disconnect(conn connection.Connection) {
	UnsubscribeAll(svr.hub, conn)
}

func (svr *Standalone) Close() {
	if svr.aofHandler != nil {
		svr.aofHandler.Close()
	}
}
